// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:collection/collection.dart';

/// Re-casing helpers for strings.
extension StringRecase on String {
  /// The capitalized version of `this`, with the first letter upper-cased and
  /// all others lower-cased.
  String get capitalized {
    if (length < 2) return toUpperCase();
    return this[0].toUpperCase() + substring(1).toLowerCase();
  }

  /// The `camelCase` version of `this`.
  String get camelCase => groupIntoWords().mapIndexed((index, word) {
    if (index == 0) return word.toLowerCase();
    return word.capitalized;
  }).join();

  /// The `param-case` version of `this`.
  String get paramCase =>
      groupIntoWords().map((word) => word.toLowerCase()).join('-');

  /// Acronyms for which we should maintain casing.
  static const _maintainCase = ['AWS'];

  /// The `PascalCase` version of `this`.
  String get pascalCase => groupIntoWords().map((word) {
    if (_maintainCase.contains(word)) {
      return word;
    }
    return word.capitalized;
  }).join();

  /// The `snake_case` version of `this`.
  String get snakeCase =>
      groupIntoWords().map((word) => word.toLowerCase()).join('_');

  // "acm-success"-> "acm success"
  static final _nonAlphaNumericChars = RegExp(r'[^A-Za-z0-9+]');

  // TESTv4 -> "TEST v4"
  static final _standaloneVLower = RegExp(r'([^a-z]{2,})v([0-9]+)');

  // TestV4 -> "Test V4"
  static final _standaloneVUpper = RegExp(r'([^A-Z]{2,})V([0-9]+)');

  // "AcmSuccess" -> "Acm Success"
  // Workaround for lack of support for lookbehinds in Safari:
  // https://caniuse.com/js-regexp-lookbehind
  static final _camelCasedWords = RegExp(r'(?=[a-z][A-Z][a-zA-Z0-9])');

  // "ACMSuccess" -> "ACM Success"
  static final _acronyms = RegExp(r'([A-Z]+)([A-Z][a-z])');

  // "s3ec2" -> "s3 ec2"
  static final _numInMiddleOrEnd = RegExp(r'([0-9])([a-zA-Z])');

  /// Splits `this` into words along word boundaries.
  List<String> groupIntoWords() {
    var result = this;

    // all non-alphanumeric characters: "acm-success"-> "acm success"
    result = result.replaceAll(_nonAlphaNumericChars, ' ');

    // if a number has a standalone v in front of it, separate it out
    result = result
        // TESTv4 -> "TEST v4"
        .replaceAllMapped(
          _standaloneVLower,
          (m) => '${m.group(1)} v${m.group(2)}',
        )
        // TestV4 -> "Test V4"
        .replaceAllMapped(
          _standaloneVUpper,
          (m) => '${m.group(1)} V${m.group(2)}',
        );

    // add a space between camelCased words: "AcmSuccess" -> "Acm Success"
    // Workaround for lack of support for lookbehinds in Safari:
    // https://caniuse.com/js-regexp-lookbehind
    var start = 0;
    result = () sync* {
      yield* _camelCasedWords.allMatches(result).map((match) {
        final end = match.start + 1;
        final substr = result.substring(start, end);
        start = end;
        return substr;
      });
      yield result.substring(start);
    }().join(' ');

    // add a space after acronyms: "ACMSuccess" -> "ACM Success"
    result = result.replaceAllMapped(
      _acronyms,
      (m) => '${m.group(1)} ${m.group(2)}',
    );

    // add space after a number in the middle of a word: "s3ec2" -> "s3 ec2"
    result = result.replaceAllMapped(
      _numInMiddleOrEnd,
      (m) => '${m.group(1)} ${m.group(2)}',
    );

    // remove extra spaces - multiple consecutive ones or those and the
    // beginning/end of words
    result = result.replaceAll(RegExp(r'\s+'), ' ').trim();

    return result.split(' ');
  }
}
